import pandas as pd
import numpy as np
import random
import matplotlib.pyplot as plt
import os
from sklearn.preprocessing import MinMaxScaler
import math



def walf(data,test,alpha=0.2,k=20):
# 创建用户索引

    user_idx = data['userId'].unique()
    num_users = len(user_idx)

    movie_idx = data['movieId'].unique()

    # 创建稀疏矩阵
    row = data['userId'].map(lambda x: np.where(user_idx == x)[0][0])
    col = data['movieId'].map(lambda x: np.where(movie_idx == x)[0][0])
    values = data['rating']

    # 创建随机初始矩阵
    user_matrix = np.random.rand(num_users, k)
    item_matrix = np.random.rand(data['movieId'].nunique(), k)
    # for i in range(data['userId'].nunique()):
    #     user_matrix[i] = [np.random.random() / math.sqrt(5) for x in range(5)]
    # for i in range(data['movieId'].nunique()):
    #     item_matrix[i] = [np.random.random() / math.sqrt(5) for x in range(5)]


    # 根据稀疏矩阵值计算权重矩阵
    sparse_matrix = np.zeros((num_users, len(movie_idx)))
    sparse_matrix[row, col] = values


    # 直接相乘的权重
    weight_matrix = np.where(sparse_matrix > 0, alpha*sparse_matrix+1, 1)



    # 迭代训练
    learning_rate = 0.000001
    num_iterations = 100
    lambda_ = 0.01  # 设置 L2 正则化参数
    for i in range(num_iterations):
        # 计算预测评分和实际评分之差
        pred = np.dot(user_matrix, item_matrix.T)
        error = values.values - pred[row, col]

    # 更新参数并添加 L2 正则化与加权
        user_matrix[row, :] += learning_rate * (
                    np.sum(error[:, np.newaxis] * (item_matrix[col, :] * weight_matrix[row, col][:, np.newaxis]), axis=0) -
                    2 * lambda_ * user_matrix[row, :])
        item_matrix += learning_rate * (
                    np.sum(error[:, np.newaxis] * (user_matrix[row, :] * weight_matrix[row, col][:, np.newaxis]), axis=0) -
                    2 * lambda_ * item_matrix)

    # 计算均方误差
        error_matrix = (pred[row, col] - values) ** 2
        weighted_error_matrix = error_matrix * weight_matrix[row, col]
        mse = np.mean(weighted_error_matrix)

        if i % 100 == 0:
            print("Iteration:", i, "Weighted MSE:", mse)


    user_matrix = pd.DataFrame(user_matrix, index=user_idx)

    p=np.dot(user_matrix, item_matrix.T)
    t=data.pivot_table(values='rating', index='userId', columns='movieId').fillna(0)
    # t=t.to_numpy()

    df = pd.DataFrame(data=p, index=t.index, columns=t.columns)
    max_row_indexes = df.idxmax(axis=1)
    max_row_indexes.to_csv("max.csv")


    pre=pd.DataFrame(p,index=user_idx,columns=data['movieId'].unique())


    test_value=test["rating"]
    test_row = test['userId'].map(lambda x: np.where(user_idx == x)[0][0])
    test_col = test['movieId'].map(lambda x: np.where(movie_idx == x)[0][0])

    error = np.mean((test_value.values - pred[test_row, test_col])**2)
    error1=np.mean(abs(test_value.values - pred[test_row, test_col]))

    return error1
#
# error=walf(data,test)
def walf1(data,test,k=20):
    user_idx = data['userId'].unique()
    num_users = len(user_idx)

    movie_idx = data['movieId'].unique()

    # 创建稀疏矩阵
    row = data['userId'].map(lambda x: np.where(user_idx == x)[0][0])
    col = data['movieId'].map(lambda x: np.where(movie_idx == x)[0][0])
    values = data['rating']

    # 创建随机初始矩阵
    user_matrix = np.random.rand(num_users, k)
    item_matrix = np.random.rand(data['movieId'].nunique(), k)








    # 根据稀疏矩阵值计算权重矩阵
    sparse_matrix = np.zeros((num_users, len(movie_idx)))
    sparse_matrix[row, col] = values


    # 评分频率
    # rating_frequency = data['movieId'].value_counts()
    # # 计算评分的逆频率权重
    # weight_matrix=np.zeros(sparse_matrix.shape)
    #
    # # 将评分的逆频率权重映射到稀疏矩阵中
    # for index,value in rating_frequency.items():
    #     indices = [i for i, x in enumerate(movie_idx) if x == index]
    #     weight_matrix[:,indices]=1/value



    # 权重填充为物品的平均值
    item_means = data.groupby('movieId')['rating'].mean()
    weight_matrix = np.zeros(sparse_matrix.shape)
    for index,value in item_means.items():
        indices = [i for i, x in enumerate(movie_idx) if x == index]
        weight_matrix[:,indices]=value



    # 迭代训练
    learning_rate = 0.0000001
    num_iterations = 100
    lambda_ = 0.01  # 设置 L2 正则化参数
    for i in range(num_iterations):
        # 计算预测评分和实际评分之差
        pred = np.dot(user_matrix, item_matrix.T)
        error = values.values - pred[row, col]

    # 更新参数并添加 L2 正则化与加权
        user_matrix[row, :] += learning_rate * (
                    np.sum(error[:, np.newaxis] * (item_matrix[col, :] * weight_matrix[row, col][:, np.newaxis]), axis=0) -
                    2 * lambda_ * user_matrix[row, :])
        item_matrix += learning_rate * (
                    np.sum(error[:, np.newaxis] * (user_matrix[row, :] * weight_matrix[row, col][:, np.newaxis]), axis=0) -
                    2 * lambda_ * item_matrix)

    # 计算均方误差
        error_matrix = (pred[row, col] - values) ** 2
        weighted_error_matrix = error_matrix * weight_matrix[row, col]
        mse = np.mean(weighted_error_matrix)

        if i % 100 == 0:
            print("Iteration:", i, "Weighted MSE:", mse)


    user_matrix = pd.DataFrame(user_matrix, index=user_idx)

    p=np.dot(user_matrix, item_matrix.T)
    t=data.pivot_table(values='rating', index='userId', columns='movieId').fillna(0)
    # t=t.to_numpy()

    df = pd.DataFrame(data=p, index=t.index, columns=t.columns)
    max_row_indexes = df.idxmax(axis=1)
    max_row_indexes.to_csv("max.csv")



    test_value=test["rating"]
    test_row = test['userId'].map(lambda x: np.where(user_idx == x)[0][0])
    test_col = test['movieId'].map(lambda x: np.where(movie_idx == x)[0][0])





    error = np.mean((test_value.values - pred[test_row, test_col])**2)


    error1=np.mean(abs(test_value.values - pred[test_row, test_col]))
    print(error1)

    return error1
def walf2(data,test,k=20):
    user_idx = data['userId'].unique()
    num_users = len(user_idx)

    movie_idx = data['movieId'].unique()

    # 创建稀疏矩阵
    row = data['userId'].map(lambda x: np.where(user_idx == x)[0][0])
    col = data['movieId'].map(lambda x: np.where(movie_idx == x)[0][0])
    values = data['rating']

    # 创建随机初始矩阵
    user_matrix = np.random.rand(num_users, k)
    item_matrix = np.random.rand(data['movieId'].nunique(), k)

    # 根据稀疏矩阵值计算权重矩阵
    sparse_matrix = np.zeros((num_users, len(movie_idx)))
    sparse_matrix[row, col] = values


    # 评分频率
    # rating_frequency = data['movieId'].value_counts()
    # # 计算评分的逆频率权重
    # weight_matrix=np.zeros(sparse_matrix.shape)
    #
    # # 将评分的逆频率权重映射到稀疏矩阵中
    # for index,value in rating_frequency.items():
    #     indices = [i for i, x in enumerate(movie_idx) if x == index]
    #     weight_matrix[:,indices]=1/value




    # 填充用户的评分
    user_means = data.groupby('userId')['rating'].mean()
    weight_matrix = np.zeros(sparse_matrix.shape)
    for index,value in user_means.items():
        indices = [i for i, x in enumerate(user_idx) if x == index]
        weight_matrix[indices,:]=value



    # 迭代训练
    learning_rate = 0.0000001
    num_iterations = 100
    lambda_ = 0.01  # 设置 L2 正则化参数
    for i in range(num_iterations):
        # 计算预测评分和实际评分之差
        pred = np.dot(user_matrix, item_matrix.T)
        error = values.values - pred[row, col]

    # 更新参数并添加 L2 正则化与加权
        user_matrix[row, :] += learning_rate * (
                    np.sum(error[:, np.newaxis] * (item_matrix[col, :] * weight_matrix[row, col][:, np.newaxis]), axis=0) -
                    2 * lambda_ * user_matrix[row, :])
        item_matrix += learning_rate * (
                    np.sum(error[:, np.newaxis] * (user_matrix[row, :] * weight_matrix[row, col][:, np.newaxis]), axis=0) -
                    2 * lambda_ * item_matrix)

    # 计算均方误差
        error_matrix = (pred[row, col] - values) ** 2
        weighted_error_matrix = error_matrix * weight_matrix[row, col]
        mse = np.mean(weighted_error_matrix)

        if i % 100 == 0:
            print("Iteration:", i, "Weighted MSE:", mse)


    user_matrix = pd.DataFrame(user_matrix, index=user_idx)

    p=np.dot(user_matrix, item_matrix.T)
    t=data.pivot_table(values='rating', index='userId', columns='movieId').fillna(0)
    # t=t.to_numpy()

    df = pd.DataFrame(data=p, index=t.index, columns=t.columns)
    max_row_indexes = df.idxmax(axis=1)
    max_row_indexes.to_csv("max.csv")



    test_value=test["rating"]
    test_row = test['userId'].map(lambda x: np.where(user_idx == x)[0][0])
    test_col = test['movieId'].map(lambda x: np.where(movie_idx == x)[0][0])

    error = np.mean((test_value.values - pred[test_row, test_col])**2)
    error1=np.mean(abs(test_value.values - pred[test_row, test_col]))
    print(error)

    return error1



def walf_getUser(data,test,alpha=0.2):


# 创建用户索引
    user_idx = data['userId'].unique()
    num_users = len(user_idx)

    movie_idx = data['movieId'].unique()

    # 创建稀疏矩阵
    row = data['userId'].map(lambda x: np.where(user_idx == x)[0][0])
    col = data['movieId'].map(lambda x: np.where(movie_idx == x)[0][0])
    values = data['rating']

    # 创建随机初始矩阵
    user_matrix = np.random.rand(num_users, 5)
    item_matrix = np.random.rand(data['movieId'].nunique(), 5)
    # for i in range(data['userId'].nunique()):
    #     user_matrix[i] = [np.random.random() / math.sqrt(5) for x in range(5)]
    # for i in range(data['movieId'].nunique()):
    #     item_matrix[i] = [np.random.random() / math.sqrt(5) for x in range(5)]


    # 根据稀疏矩阵值计算权重矩阵
    sparse_matrix = np.zeros((num_users, len(movie_idx)))
    sparse_matrix[row, col] = values


    # 直接相乘的权重
    weight_matrix = np.where(sparse_matrix > 0, alpha*sparse_matrix+1, 1)



    # 迭代训练
    learning_rate = 0.0000001
    num_iterations = 100
    lambda_ = 0.01  # 设置 L2 正则化参数
    for i in range(num_iterations):
        # 计算预测评分和实际评分之差
        pred = np.dot(user_matrix, item_matrix.T)
        error = values.values - pred[row, col]

    # 更新参数并添加 L2 正则化与加权
        user_matrix[row, :] += learning_rate * (
                    np.sum(error[:, np.newaxis] * (item_matrix[col, :] * weight_matrix[row, col][:, np.newaxis]), axis=0) -
                    2 * lambda_ * user_matrix[row, :])
        item_matrix += learning_rate * (
                    np.sum(error[:, np.newaxis] * (user_matrix[row, :] * weight_matrix[row, col][:, np.newaxis]), axis=0) -
                    2 * lambda_ * item_matrix)

    # 计算均方误差
        error_matrix = (pred[row, col] - values) ** 2
        weighted_error_matrix = error_matrix * weight_matrix[row, col]
        mse = np.mean(weighted_error_matrix)

        # if i % 100 == 0:
            # print("Iteration:", i, "Weighted MSE:", mse)


    user_matrix = pd.DataFrame(user_matrix, index=user_idx)

    p=np.dot(user_matrix, item_matrix.T)

    # t=t.to_numpy()
    # print(t)

    df = pd.DataFrame(data=p, index=user_idx, columns=movie_idx)
    max_row_indexes = df.idxmax(axis=1)
    max_row_indexes.to_csv("max.csv")


    pre=pd.DataFrame(user_matrix,index=user_idx)

    test_value=test["rating"]
    test_row = test['userId'].map(lambda x: np.where(user_idx == x)[0][0])
    test_col = test['movieId'].map(lambda x: np.where(movie_idx == x)[0][0])

    error = np.mean((test_value.values - pred[test_row, test_col])**2)
    print(error)

    return pre,df,error
def walf_all(data,test,alpha=0.2):


# 创建用户索引
    user_idx = data['userId'].unique()
    num_users = len(user_idx)

    movie_idx = data['movieId'].unique()

    # 创建稀疏矩阵
    row = data['userId'].map(lambda x: np.where(user_idx == x)[0][0])
    col = data['movieId'].map(lambda x: np.where(movie_idx == x)[0][0])
    values = data['rating']

    # 创建随机初始矩阵
    user_matrix = np.random.rand(num_users, 5)
    item_matrix = np.random.rand(data['movieId'].nunique(), 5)
    # for i in range(data['userId'].nunique()):
    #     user_matrix[i] = [np.random.random() / math.sqrt(5) for x in range(5)]
    # for i in range(data['movieId'].nunique()):
    #     item_matrix[i] = [np.random.random() / math.sqrt(5) for x in range(5)]


    # 根据稀疏矩阵值计算权重矩阵
    sparse_matrix = np.zeros((num_users, len(movie_idx)))
    sparse_matrix[row, col] = values


    # 直接相乘的权重
    weight_matrix = np.where(sparse_matrix > 0, alpha*sparse_matrix+1, 1)



    # 迭代训练
    learning_rate = 0.0000001
    num_iterations = 100
    lambda_ = 0.01  # 设置 L2 正则化参数
    for i in range(num_iterations):
        # 计算预测评分和实际评分之差
        pred = np.dot(user_matrix, item_matrix.T)
        error = values.values - pred[row, col]

    # 更新参数并添加 L2 正则化与加权
        user_matrix[row, :] += learning_rate * (
                    np.sum(error[:, np.newaxis] * (item_matrix[col, :] * weight_matrix[row, col][:, np.newaxis]), axis=0) -
                    2 * lambda_ * user_matrix[row, :])
        item_matrix += learning_rate * (
                    np.sum(error[:, np.newaxis] * (user_matrix[row, :] * weight_matrix[row, col][:, np.newaxis]), axis=0) -
                    2 * lambda_ * item_matrix)

    # 计算均方误差
        error_matrix = (pred[row, col] - values) ** 2
        weighted_error_matrix = error_matrix * weight_matrix[row, col]
        mse = np.mean(weighted_error_matrix)

        # if i % 100 == 0:
            # print("Iteration:", i, "Weighted MSE:", mse)


    user_matrix = pd.DataFrame(user_matrix, index=user_idx)

    p=np.dot(user_matrix, item_matrix.T)

    # t=t.to_numpy()
    # print(t)

    df = pd.DataFrame(data=p, index=user_idx, columns=movie_idx)
    max_row_indexes = df.idxmax(axis=1)
    max_row_indexes.to_csv("max.csv")

    pre_u=pd.DataFrame(user_matrix,index=user_idx)
    pre_i=pd.DataFrame(item_matrix,index=movie_idx)

    test_value=test["rating"]
    test_row = test['userId'].map(lambda x: np.where(user_idx == x)[0][0])
    test_col = test['movieId'].map(lambda x: np.where(movie_idx == x)[0][0])

    error = np.mean((test_value.values - pred[test_row, test_col])**2)
    print(error)

    return pre_i,pre_i,df,error
def walf_iter(data,test,alpha=0.2,p='',q=''):

# 创建用户索引
    user_idx = np.sort(data['userId'].unique())
    num_users = len(user_idx)


    movie_idx = np.sort(data['movieId'].unique())

    # 创建稀疏矩阵
    row = data['userId'].map(lambda x: np.where(user_idx == x)[0][0])
    col = data['movieId'].map(lambda x: np.where(movie_idx == x)[0][0])
    values = data['rating']

    # 创建随机初始矩阵
    user_matrix = p
    item_matrix = q

    # 根据稀疏矩阵值计算权重矩阵
    sparse_matrix = np.zeros((num_users, len(movie_idx)))
    sparse_matrix[row, col] = values


    # 直接相乘的权重
    weight_matrix = np.where(sparse_matrix > 0, alpha*sparse_matrix+1, 1)

    print(weight_matrix)

    # 迭代训练
    learning_rate = 0.0000001
    num_iterations = 50
    lambda_ = 0.01  # 设置 L2 正则化参数
    for i in range(num_iterations):
        # 计算预测评分和实际评分之差
        pred = np.dot(user_matrix, item_matrix.T)
        error = values.values - pred[row, col]

    # 更新参数并添加 L2 正则化与加权
        user_matrix[row, :] += learning_rate * (
                    np.sum(error[:, np.newaxis] * (item_matrix[col, :] * weight_matrix[row, col][:, np.newaxis]), axis=0) -
                    2 * lambda_ * user_matrix[row, :])
        item_matrix += learning_rate * (
                    np.sum(error[:, np.newaxis] * (user_matrix[row, :] * weight_matrix[row, col][:, np.newaxis]), axis=0) -
                    2 * lambda_ * item_matrix)

    # 计算均方误差
        error_matrix = (pred[row, col] - values) ** 2
        weighted_error_matrix = error_matrix * weight_matrix[row, col]

        mse = np.mean(weighted_error_matrix)

        # if i % 100 == 0:
            # print("Iteration:", i, "Weighted MSE:", mse)


    # user_matrix = pd.DataFrame(user_matrix, index=user_idx)

    p=np.dot(user_matrix, item_matrix.T)

    # t=t.to_numpy()
    # print(t)

    df = pd.DataFrame(data=p, index=user_idx, columns=movie_idx)
    max_row_indexes = df.idxmax(axis=1)
    max_row_indexes.to_csv("max.csv")


    pre=pd.DataFrame(user_matrix,index=user_idx)

    test_value=test["rating"]
    test_row = test['userId'].map(lambda x: np.where(user_idx == x)[0][0])
    test_col = test['movieId'].map(lambda x: np.where(movie_idx == x)[0][0])

    error = np.mean((test_value.values - pred[test_row, test_col])**2)
    print(error)

    return user_matrix,item_matrix,error
